//
// Created by LiuYou on 2021/10/24.
//

#include <csignal>
#include <unistd.h>
#include <cstdlib>
#include <cstdio>
#include <pthread.h>
#include "AnyTimer.hpp"


class Timer {
public:
    Timer(int sec, void (* jobFunc)(void*), void* funcArgs) :
            sec( sec ), jobFunc( jobFunc ), funcArgs( funcArgs ), pos( -1 ) {
        pthread_mutex_init( &mutex, nullptr );
    }

    ~Timer() {
        pthread_mutex_destroy( &mutex );
    }

public:
    int sec;
    Func jobFunc;
    void* funcArgs;
    int pos;

public:
    pthread_t tid{};

public:
    /*
     * 保护字段 sec。
     * 这个版本中不用,
     * 现在这个版本需要了。
     */
    pthread_mutex_t mutex{};

public:
    /*
     * 解决盲等。
     */
    pthread_cond_t cond{};

public:
    /*
     * 当前任务是否执行。
     */
    bool isExecute = false;

};


static Timer* jobArray[JOB_MAX];
static pthread_mutex_t MUTEX_JOB_ARRAY = PTHREAD_MUTEX_INITIALIZER;


static bool isInited = false;

static pthread_t alarmTid;

static pthread_t clearTid;


static int getFreePosUnlocked() {
    for (int i = 0; i < JOB_MAX; ++i) {
        if (jobArray[i] == nullptr) {
            return i;
        }
    }
    return -1;
}


static void moduleLoad() {


    pthread_create(
            &alarmTid, nullptr,
            [](void* args) -> void* {

                while (true) {

                    pthread_mutex_lock( &MUTEX_JOB_ARRAY );
                    for (int i = 0; i < JOB_MAX; ++i) {
                        if (jobArray[i] != nullptr) {

                            pthread_mutex_lock( &jobArray[i]->mutex );
                            --(jobArray[i]->sec);
                            if (jobArray[i]->sec == 0) {
                                pthread_cond_broadcast( &jobArray[i]->cond );
                            }
                            pthread_mutex_unlock( &jobArray[i]->mutex );

                        }
                    }
                    pthread_mutex_unlock( &MUTEX_JOB_ARRAY );

                    sleep( 1 );
                }


                pthread_exit( nullptr );
            },
            nullptr );

//    pthread_create(
//            &clearTid, nullptr,
//            [](void* args) -> void* {
//
//
//                while (true) {
//                    pthread_mutex_lock( &MUTEX_JOB_ARRAY );
//                    for (int i = 0; i < JOB_MAX; ++i) {
//                        if (jobArray[i] != nullptr) {
//                            if (jobArray[i]->isExecute == true) {
//                                delete jobArray[i];
//                                jobArray[i] = nullptr;
//                            }
//                        }
//                    }
//                    pthread_mutex_unlock( &MUTEX_JOB_ARRAY );
//                }
//
//
//                pthread_exit( nullptr );
//            },
//            nullptr );


    atexit( [] {

        fprintf( stdout, "执行模块卸载函数。\n" );


        pthread_cancel( alarmTid );
        pthread_join( alarmTid, nullptr );

//        pthread_cancel( clearTid );
//        pthread_join( clearTid, nullptr );


        pthread_mutex_lock( &MUTEX_JOB_ARRAY );
        for (int i = 0; i < JOB_MAX; ++i) {
            if (jobArray[i] != nullptr) {
                delete jobArray[i];
            }
        }
        pthread_mutex_unlock( &MUTEX_JOB_ARRAY );


        pthread_mutex_destroy( &MUTEX_JOB_ARRAY );

    } );
}


/*

 返回值
         >=  0       成功, 返回任务ID。
         ==  -1      失败, 参数非法。
         ==  -2      失败, 不能在支持其它任务了, 达到了计时器支持任务的上限。
         ==  -3      失败, malloc();调用失败。
 */
int addJob(int sec, Func jobFunc, void* args) {


    if (!isInited) {
        moduleLoad();
        isInited = true;
    }

    Timer* ptrTimer = new Timer( sec, jobFunc, args );

    pthread_mutex_lock( &MUTEX_JOB_ARRAY );
    int pos = getFreePosUnlocked();
    if (pos < 0) {
        pthread_mutex_unlock( &MUTEX_JOB_ARRAY );
        delete ptrTimer;
        return -1;
    }

    ptrTimer->pos = pos;
    jobArray[pos] = ptrTimer;
    pthread_mutex_unlock( &MUTEX_JOB_ARRAY );


    /*
     * 新建一个线程等待运行这个定时器任务。
     */
    int errNum = pthread_create(
            &ptrTimer->tid, nullptr,
            [](void* args) -> void* {

                pthread_detach( pthread_self());

                Timer* me = static_cast<Timer*>(args);

                pthread_mutex_lock( &me->mutex );
                while (me->sec != 0) {
//                    pthread_mutex_unlock( &me->mutex );
//                    sched_yield();
//                    pthread_mutex_lock( &me->mutex );
                    pthread_cond_wait( &me->cond, &me->mutex );
                }
                pthread_mutex_unlock( &me->mutex );

                me->jobFunc( me->funcArgs );


//                pthread_mutex_lock( &MUTEX_JOB_ARRAY );
//                /*todo 注意:
//                 * 退出的时候, 一定及时将自己在 jobArray[] 中的位置让出来。
//                 */
////                jobArray[me->pos] = nullptr;
//                jobArray[me->pos]->isExecute = true;
//                pthread_mutex_unlock( &MUTEX_JOB_ARRAY );


                pthread_mutex_lock( &MUTEX_JOB_ARRAY );
                /*todo 注意:
                 * 退出的时候, 一定及时将自己在 jobArray[] 中的位置让出来。
                 */
                Timer* pTimer = jobArray[me->pos];
                jobArray[me->pos]->isExecute = true;
                jobArray[me->pos] = nullptr;
                delete pTimer;
                pthread_mutex_unlock( &MUTEX_JOB_ARRAY );


                pthread_exit( nullptr );
            },
            ptrTimer );
    if (errNum != 0) {
        fprintf( stderr, "pthread_create(): 失败了。\n" );
        exit( EXIT_FAILURE );
    }


    return pos;
}

/**
 * @brief
 * @param jobId
 * @return
 * <p>      ==  0       成功, 指定任务成功取消。
 * <p>      ==  -1      失败, 传入参数非法。
 * <p>      ==  -2      失败, 指定任务已经完成。
 * <p>      ==  -3      失败, 指定任务重复取消。
 */
int cancelJob(int jobId) {

    return 0;
}

/**
 * @brief
 * @param jobId
 * @return
 * <p>      ==  0       成功, 指定任务成功释放。
 * <p>      ==  -1      失败, 传入参数非法。
 */
int waitJob(int jobId) {
    Timer* me = jobArray[jobId];

    pthread_mutex_lock( &MUTEX_JOB_ARRAY );
    if (jobArray[jobId] == nullptr) {
        pthread_mutex_unlock( &MUTEX_JOB_ARRAY );
        return 0;
    }
    jobArray[jobId] = nullptr;
    pthread_mutex_unlock( &MUTEX_JOB_ARRAY );


    delete me;
    return 0;
}


/* ************************************************************ */


int pauseJob(int jobId) {
    return 0;
}

int resumeJob(int jobId) {
    return 0;
}
